package model;

import java.util.ArrayList;
import java.util.List;

public class Field {

	private Tile[][] tiles;
	private Endpoint[] endpoints;
	private ArrayList<Integer> connectedVoltages = new ArrayList<Integer>();

	private boolean solved = false;

	/**
	 * @return the tiles
	 */
	public Tile[][] getTiles() {
		return tiles;
	}

	/**
	 * @param tiles
	 *            the tiles to set
	 */
	public void setTiles(Tile[][] tiles) {
		this.tiles = tiles;
	}

	public Endpoint[] getEndpoints() {
		return this.endpoints;
	}

	public void setEndpoints(Endpoint[] endpoints) {
		this.endpoints = endpoints;
	}

	public void checkForSolved() {
		this.solved = true;
		this.connectedVoltages = new ArrayList<Integer>();
		for (Endpoint outpoint : endpoints) {
			if (outpoint.getType() == EndpointType.OUTPUT) {
				System.out.println("check Output " + outpoint.getType() + ", "
						+ outpoint.getVoltageId());
				for(Endpoint inpoint:endpoints) {
					if(inpoint.getVoltageId() == outpoint.getVoltageId() && inpoint.getType() == EndpointType.INPUT) {
						if (!isEndpointConected(
								getEndpointByIndex(outpoint.getPositionId()),
								getEndpointByIndex(inpoint.getPositionId()), null)) {
							solved = false;
						} else {
							System.out.println("connected");
							this.connectedVoltages.add(outpoint.getVoltageId());
						}
					}
				}
			}
		}
		if(solved) {
		System.out.println("solved!!!");
		}
	}

	private int[] getEndpointByIndex(int index) {
		int[] position = new int[3];
		int count = ((tiles.length - 1) * 2 + (tiles[0].length - 1) * 2 + 4);
		if (index < tiles[0].length) {
			position[0] = tiles.length - 1;
			position[1] = index;
			position[2] = 1;
		} else if (index < tiles[0].length + tiles.length) {
			int i = (tiles[0].length + tiles.length - 1) - index;
			position[0] = i;
			position[1] = tiles[0].length - 1;
			position[2] = 4;
		} else if (index < tiles[0].length * 2 + tiles.length) {
			int i = (tiles[0].length * 2 + tiles.length - 1) - index;
			position[0] = 0;
			position[1] = i;
			position[2] = 7;
		} else if (index < tiles[0].length * 2 + tiles.length * 2) {
			int i = index - (tiles[0].length * 2 + tiles.length);
			position[0] = i;
			position[1] = 0;
			position[2] = 10;
		}
		return position;
	}

	private boolean isEndpointConected(int[] other, int[] position, ArrayList<int[]> passed) {
		if (passed == null) {
			passed = new ArrayList<int[]>();
		}
		//System.out.println("\t" + other[0] + "x" + other[1] + " " + other[2] + " - " + position[0] + "x" + position[1] + " " + position[2]);
		int[][] connections = this.tiles[other[0]][other[1]].getConnections();
		for (int conni = 0; conni < connections.length; conni++) {
			int[] pass = new int[] { other[0], other[1], conni };
			if (!passedContains(passed, pass)) {
				passed.add(pass);
				int[] connection = connections[conni];
				//System.out.println("\t\t" + connection[0] + " " + connection[1] + " " + connection[2]);
				if (contains(connection, other[2])) {
					if(other[0] == position[0] && other[1] == position[1] && contains(connection, position[2])) {
						//System.out.println("TRUE");
						return true;
					}
					for (int conn : connection) {
						if(conn != other[2]) {
							int[] neighbor = getNeighbor(new int[]{other[0], other[1], conn});
							if(neighbor != null) {
								//System.out.println("\t\tNeighbor: " + neighbor[0] + " " + neighbor[1] + " " + neighbor[2]);
								if(isEndpointConected(neighbor, position, passed)) {
									return true;
								}
							}
						}
					}
				}
			}
		}
		return false;
	}

	private boolean contains(int[] array, int value) {
		for (int val : array) {
			//System.out.println("." + val + " == " + value);
			if (val == value) {
				return true;
			}
		}
		return false;
	}

	private boolean passedContains(ArrayList<int[]> passed, int[] p) {
		for (int[] pass : passed) {
			if (pass[0] == p[0] && pass[1] == p[1] && pass[2] == p[2]) {
				return true;
			}
		}
		return false;
	}

	private int[] getNeighbor(int[] other) {
		switch(other[2]) {
		case 0:
		case 1:
		case 2:
			if(other[0] < tiles.length - 1) {
				return new int[]{other[0] + 1, other[1], 8 - other[2]};
			}
			break;
		case 3:
		case 4:
		case 5:
			if(other[1] < tiles[0].length - 1) {
				return new int[]{other[0], other[1] + 1, 14 - other[2]};
			}
			break;
		case 6:
		case 7:
		case 8:
			if(other[0] > 0) {
				return new int[]{other[0] - 1, other[1], 8 - other[2]};
			}
			break;
		case 9:
		case 10:
		case 11:
			if(other[1] > 0) {
				return new int[]{other[0], other[1] - 1, 14 - other[2]};
			}
			break;
		}
		return null;
	}

	private List<int[]> listEndpointPositions(int[] start) {
		return null;
	}

	public boolean isSolved() {
		return this.solved;
	}
	
	public int[] getConnectedVoltages() {
		Integer[] ints = this.connectedVoltages.toArray(new Integer[this.connectedVoltages.size()]);
		int[] conn = new int[ints.length];
		for(int i = 0; i < conn.length; i++) {
			conn[i] = ints[i];
		}
		return conn;
	}
}
